import { debounce } from '@peng_kai/kit/libs/lodash-es';
import { useEventListener } from '@peng_kai/kit/libs/vueuse';
import { getHashParams, setHashParams } from './helpers';

function useUrlHashParams() {
  const hashParams = shallowRef<URLSearchParams>();

  const parseHashParams = debounce((url: string) => {
    hashParams.value = getHashParams(url);
  }, 300, { leading: false, trailing: true });

  useEventListener(window, 'hashchange', () => parseHashParams(location.href));
  parseHashParams(location.href);

  return hashParams;
}

function autoResetValue<T>(defaultValue: T, delay = 500) {
  let value = defaultValue;
  let timer = -1;

  onBeforeUnmount(() => clearTimeout(timer));

  return {
    get value() {
      return value;
    },
    set value(newValue: T) {
      value = newValue;
      timer = setTimeout(() => {
        value = defaultValue;
      }, delay);
    },
  };
}

type PresetCondition = [type: 'modal', cond: string] | [type: 'keys', cond: string[]] | [type: 'authed'] | [type: 'unauthed'];
type CustomCondition = (hashParams: URLSearchParams) => boolean;

function getCondFn(cond: PresetCondition | CustomCondition) {
  if (Array.isArray(cond)) {
    if (cond[0] === 'modal') {
      return (hashParams: URLSearchParams) => hashParams.get('modal') === cond[1];
    }
    else if (cond[0] === 'keys') {
      return (hashParams: URLSearchParams) => {
        const allKeys = [...hashParams.keys()];
        return cond[1].every(x => allKeys.includes(x));
      };
    }
    else if (cond[0] === 'authed') {
      return () => useAuthed().value;
    }
    else if (cond[0] === 'unauthed') {
      return () => !useAuthed().value;
    }

    return () => false;
  }
  else {
    return cond;
  }
}

/**
 * BUG: 弹窗关闭时会清掉打开之前的 URL Search Params
 */
export function useModalBindHash(options: {
  modalName: string;
  conds?: CustomCondition[];
  requireAuth?: boolean;
  requireParamKeys?: string[];
  isOpenedReplace?: boolean;
  isClosedReplace?: boolean;
  isOpen: () => boolean;
  open: (hashParams: URLSearchParams) => void;
  close: () => void;
}) {
  const { modalName, conds, requireAuth, requireParamKeys, isOpenedReplace = true, isClosedReplace = true, isOpen, open, close } = options;
  const hashParams = useUrlHashParams();
  const condFnList = reactive<Array<(hashParams: URLSearchParams) => boolean>>([]);
  const activeFrom = autoResetValue<'call' | 'url' | undefined>(undefined, 500);

  condFnList.push(getCondFn(['modal', modalName]));
  requireAuth === true && condFnList.push(getCondFn(['authed']));
  requireAuth === false && condFnList.push(getCondFn(['unauthed']));
  requireParamKeys && condFnList.push(getCondFn(['keys', requireParamKeys]));
  conds?.forEach(cond => condFnList.push(getCondFn(cond)));

  watch(isOpen, (opened) => {
    const params = getHashParams();

    if (activeFrom.value === 'url') {
      return;
    }

    activeFrom.value = 'call';

    if (opened) {
      if (params.get('modal') !== modalName) {
        params.set('modal', modalName);
        setHashParams(params, isOpenedReplace);
      }
    }
    else {
      if (params.get('modal') === modalName) {
        params.delete('modal');
        requireParamKeys?.forEach(key => params.delete(key));
        setHashParams(params, isClosedReplace);
      }
    }
  }, { flush: 'pre' });

  watchEffect(() => {
    const canOpen = hashParams.value ? condFnList.every(cond => cond(hashParams.value!)) : false;

    if (activeFrom.value === 'call') {
      return;
    }

    if (!isOpen() && canOpen) {
      activeFrom.value = 'url';
      open(hashParams.value!);
    }
    else if (isOpen() && !canOpen) {
      activeFrom.value = 'url';
      close();
    }
  }, { flush: 'post' });
}
